﻿<#
.SYNOPSIS
Execute a command inside a management execpted result structure and errors catching

.DESCRIPTION
Execute a command inside a management execpted result structure and errors catching

.PARAMETER source
Required. the xml element which contains the database connection defintion
#>
function Invoke-ManagedCommand
{
    [cmdletbinding(DefaultParameterSetName="none")]
	param
	(
		[parameter(Mandatory = $true)]
		[System.String]
		$CommandToExecute,

        [Parameter(ParameterSetName="RemoteCommand",Mandatory=$true,ValueFromPipeline=$false)] 
		[System.Management.Automation.Runspaces.PSSession]
        $PsSession

	)
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Invoke-ManagedCommand for $CommandToExecute"
    }
    End
    {
        write-verbose "End Invoke-ManagedCommand for $CommandToExecute"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process
    {

        $resultAction =  New-Object PSObject -Property @{CommandToExecute=$CommandToExecute
                                                         Status=$false
                                                         InternalResult=$null
                                                         ExitCode = -1
                                                         Exception=""
                                                         Warning = ""
                                                         Start=$(Get-date)
                                                         End=$null
                                                         Duration=$null
                                                        }

        try
        {
            $scriptBlockCommand = [ScriptBlock]::Create($CommandToExecute)
            $lastexitcode = 0

            if ($PSCmdlet.ParameterSetName -eq "RemoteCommand")
            {
                if ($PsSession.State -ne 'Opened')
                {
                    throw New-Object System.Exception "the session id $($PsSession.Id) on computer $($PsSession.ComputerName) is not opened"
                }

                if ($PsSession.Availability -ne 'Available')
                {
                    throw New-Object System.Exception "the session id $($PsSession.Id) on computer $($PsSession.ComputerName) is not available"
                }

                $resultAction.InternalResult = Invoke-Command -Session $PsSession -ScriptBlock $scriptBlockCommand -ErrorVariable errorAction -ErrorAction Continue -WarningVariable warningAction

            }
            else
            {
                $resultAction.InternalResult = Invoke-Command -ScriptBlock $scriptBlockCommand -ErrorVariable errorAction -ErrorAction Stop -WarningVariable warningAction
            }

            $resultAction.ExitCode = $lastexitcode
            $lastexitcode = 0

        }
        catch [Exception]
        {
            $resultAction.ExitCode = -1
            $errorAction = $_.Exception                            
        }

        if ($errorAction -ne $null)
        {
            Write-Verbose "Detected Error in ErrorAction variable : $errorAction"
            $resultAction.Exception = $errorAction
            if (($resultAction.ExitCode -eq $null) -or ($resultAction.ExitCode -eq 0)) {$resultAction.ExitCode=-1 }
        }
                        
        if ($warningAction -ne $null)
        {
            Write-Verbose "Detected Warning in WarningAction variable : $warningAction"
            $resultAction.Warning = $warningAction                            
        }

        if ($resultAction.ExitCode -eq $null) {$resultAction.ExitCode=0 }

        $resultAction.End = $(get-Date)
        $resultAction.Duration = $(($resultAction.End - $resultAction.Start).ToString())
        $resultAction.Status = ($resultAction.ExitCode -eq 0) -or ($resultAction.ExitCode -eq $null)
        Write-Verbose "Command Execution is finised and status is retrieved : $($resultAction.Status)"

        Write-Output $resultAction
    }
}

function Invoke-TerminatingError
{
    [cmdletbinding(DefaultParameterSetName="WithMessage")]
    param
    (
        [Parameter(ParameterSetName="WithMessage",Mandatory=$true,ValueFromPipeline=$false)] 
        [Parameter(ParameterSetName="WithException",Mandatory=$true,ValueFromPipeline=$false)] 
        [String]$errorId,
        
        [Parameter(ParameterSetName="WithMessage",Mandatory=$true,ValueFromPipeline=$false)] 
        [String]$errorMessage,

        [Parameter(ParameterSetName="WithException",Mandatory=$true,ValueFromPipeline=$false)] 
        [Exception]$exception,

        [Parameter(ParameterSetName="WithMessage",Mandatory=$true,ValueFromPipeline=$false)] 
        [Parameter(ParameterSetName="WithException",Mandatory=$true,ValueFromPipeline=$false)] 
        [System.Management.Automation.ErrorCategory]$errorCategory
    )
    
    $exception   = New-Object System.InvalidOperationException $errorMessage 
    $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null
    $PSCmdlet.ThrowTerminatingError($errorRecord)
}


function New-ResultActionObject
{
        New-Object PSObject -Property @{CommandToExecute=$null
                                        Status=$false
                                        InternalResult=$null
                                        ExitCode = -1
                                        Exception=""
                                        Warning = ""
                                        Start=$(Get-date)
                                        End=$null
                                        Duration=$null
                                    }
}


function Assert-ObjectProperty
{
    [cmdletbinding(DefaultParameterSetName="singleProperty")]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject]$SourceObject,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String]$ObjectIdentifier,


        [Parameter(ParameterSetName="singleProperty",Mandatory=$true,ValueFromPipeline=$false)]
        [String]$PropertyName,


        [Parameter(ParameterSetName="MultipleProperty",Mandatory=$true,ValueFromPipeline=$false)]
        [String[]]$PropertiesName
    )
    Process
    {

        $propertyToCheck = @()
        if ($PSCmdlet.ParameterSetName -eq 'singleProperty')
        {
            $propertyToCheck += $PropertyName
        }
        else
        {
            $PropertiesName | ForEach-Object {$propertyToCheck += $_}
        }


        $errors = @()
        $propertyToCheck | ForEach-Object {
            if (-not $SourceObject.$($_))
            {
                $errors += "Missing Property $($_) or property is null"
            }
        }

        if ($errors.Length -gt 0)
        {
            $errorMessage = "Object identified by $ObjectIdentifier has missing properties : "
            $errors | ForEach-Object {$errorMessage +="`n $($_)"}

            Invoke-TerminatingError -errorId "MISSING PROPERTY" -errorMessage $errorMessage -errorCategory InvalidArgument
        }

    }
}
